import { Helper } from "./helper";

class ooo {
  public Data: bigint[];
  public totalbytes_high: bigint;
  public totalbytes_low: bigint;
  public used: number;
  public used_one: number;
  public size: number;
}
export class ulong_buf_reverse {
  public constructor(o: ooo) {
    this.Oo = o;
  }
  public Oo: ooo;
  public SetByte(value: number, position: number) {
    this.Oo.Data[position >>> 3] |= BigInt(value) << BigInt((((position & 0b111)) << 3) & 0xff);
  }
  public ToNext() {
    this.Oo.used++;
    this.Oo.used_one = 0;
  }
  public * Push(bytes: number[], offset: number, length: number, offset_last: number) {
    let i = offset >>> 0;
    let temp: bigint;
    let x, y;

    do {
      x = this.Oo.used_one;
      if (x != 0)
      {
          x = (0b111 - x);
          var max = x - length + 1;
          if (max < 0) max = 0;
          for (y = this.Oo.used_one; x > max; x--, y++, i++)
          {
              this.Oo.Data[this.Oo.used] |= BigInt(bytes[i]) << BigInt(y << 3);
              this.Oo.used_one++;
              if (length == i) break;
          }
          if (x != 0) break;
          this.Oo.Data[this.Oo.used] |= BigInt(bytes[i++]) << BigInt(56);
          this.Oo.used_one = 0;
          this.Oo.used++;
          if (this.Oo.used >= this.Oo.size - offset_last)
          {
              this.Oo.used = 0;
              yield i;
          }
      }
      else if (this.Oo.totalbytes_low != BigInt(0) && this.Oo.totalbytes_high != BigInt(0) && this.Oo.used >= this.Oo.size - offset_last) {
        this.Oo.used = 0;
        yield i;
      }
      temp = Helper.ulong(this.Oo.totalbytes_low + BigInt(length));
      if (temp < this.Oo.totalbytes_low) {
        this.Oo.totalbytes_high++;
      }
      this.Oo.totalbytes_low = temp;
      this.Oo.used_one = Number(this.Oo.totalbytes_low & BigInt(0b111));

      for (; i + 7 < length;) {
        this.Oo.Data[this.Oo.used] = (BigInt(bytes[i]) | (BigInt(bytes[i + 1]) << BigInt(8)) | (BigInt(bytes[i + 2]) << BigInt(16)) | (BigInt(bytes[i + 3]) << BigInt(24)) | (BigInt(bytes[i + 4]) << BigInt(32)) | (BigInt(bytes[i + 5]) << BigInt(40)) | (BigInt(bytes[i + 6]) << BigInt(48)) | (BigInt(bytes[i + 7]) << BigInt(56)));
        this.Oo.used++;
        i += 8;
        if (this.Oo.used >= this.Oo.size - offset_last) {
          this.Oo.used = 0;
          yield i;
        }
      }

      x = Number(this.Oo.totalbytes_low & BigInt(0b111));
      if (x != 0) {
        this.Oo.Data[this.Oo.used] = BigInt(0);
        for (y = 0; x > 0; x--, y++, i++) {
          this.Oo.Data[this.Oo.used] |= BigInt(bytes[i]) << BigInt(y << 3);
          if (length == i) break;
        }
      }
    } while (false);
  }
  public PushNext(b: number) {
    if ((this.Oo.totalbytes_low & BigInt(0b111)) != BigInt(0))
      this.Oo.Data[this.Oo.used] |= BigInt(b) << BigInt(Number(this.Oo.totalbytes_low & BigInt(0b111)) << 3);
    else this.Oo.Data[this.Oo.used] = BigInt(b);
    this.Oo.used++;
    this.Oo.used_one = 0;
    if (this.Oo.used >= this.Oo.size)
      this.Oo.used = 0;
  }
  public PushTotal() {
    this.Oo.Data[this.Oo.used++] = this.Oo.totalbytes_low << BigInt(3);
    this.Oo.Data[this.Oo.used++] = (this.Oo.totalbytes_high << BigInt(3)) | ((this.Oo.totalbytes_low & BigInt(0xfff0000000000000)) >> BigInt(52));
    this.Oo.used_one = 0;
    if (this.Oo.used >= this.Oo.size) {
      this.Oo.used = 0;
    }
  }

  public PushTotal64() {
    this.Oo.Data[this.Oo.used++] = this.Oo.totalbytes_low << BigInt(3);
    this.Oo.used_one = 0;
    if (this.Oo.used >= this.Oo.size) {
      this.Oo.used = 0;
    }
  }
  public Fill(x: number, offset_last: number) {
    for (; this.Oo.used < this.Oo.size - offset_last; this.Oo.used++) {
      this.Oo.Data[this.Oo.used] = BigInt(x);
    }
    this.Oo.used_one = 0;
    if (this.Oo.used >= this.Oo.size) {
      this.Oo.used = 0;
    }
  }
  public PushNext4(x: number) {
    this.Oo.Data[this.Oo.used++] = BigInt(x);
    this.Oo.used_one = 0;
    if (this.Oo.used >= this.Oo.size) {
      this.Oo.used = 0;
    }
  }
  public IsFULL(offset: number): boolean {
    // 最后一个
    if (this.Oo.used == 0) return this.Oo.totalbytes_low > 7;
    // 沾左边
    else {
      let r: boolean;
      if (this.Oo.used + offset == this.Oo.size) r = this.Oo.used_one != 0;
      // 中间
      else r = this.Oo.used + offset > this.Oo.size;
      // 填充之后的数据, be已经满了!
      if (r) this.Fill(0, 0);
      return r;
    }
  }
  public get Used(): number {
    return this.Oo.used;
  }
  public get UsedBytes(): number {
    return (this.Oo.used << 3) + this.Oo.used_one;
  }
  public static New(size: number): ulong_buf_reverse {
    let buf = new ooo();
    buf.Data = new Array(size);
    for (let i = 0; i < size; i++) buf.Data[i] = BigInt(0);
    buf.size = size;
    buf.totalbytes_low = BigInt(0);
    buf.totalbytes_high = BigInt(0);
    buf.used = 0;
    buf.used_one = 0;
    return new ulong_buf_reverse(buf);
  }
}